/*
 * Copyright (C) 2019
 * <tanghaifeng-gz@loongson.cn> <pengren.mcu@qq.com>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 *
 */
#include <common.h>
#include <dm.h>
#include <clk.h>
#include <clk-uclass.h>
#include <asm/io.h>
#include <div64.h>

#include <mach/ls3x.h>
#include <mach/regs-clk.h>

DECLARE_GLOBAL_DATA_PTR;

static void calc_clocks(void)
{
	u64 ctrl = 0, pll;
	unsigned int l1div_out, l1div_loopc, l1div_ref;
	unsigned int l2div_out, l2div_loopc, l2div_ref;
	unsigned int mult, div;

	/* node cpu clk */
	ctrl = (u64)readl(LOONGSON_CONF_REG(0x1b0));
	ctrl = ctrl | ((u64)readl(LOONGSON_CONF_REG(0x1b0) + 4) << 32);
	l1div_out = (ctrl >> NODE_L1DIV_OUT_SHIFT) & NODE_L1DIV_OUT_MARK;
	l1div_loopc = (ctrl >> NODE_L1DIV_LOOPC_SHIFT) & NODE_L1DIV_LOOPC_MARK;
	l1div_ref = (ctrl >> NODE_L1DIV_REF_SHIFT) & NODE_L1DIV_REF_MARK;

	l2div_loopc = (ctrl >> NODE_L2DIV_LOOPC_SHIFT) & NODE_L2DIV_LOOPC_MARK;
	l2div_ref = (ctrl >> NODE_L2DIV_REF_SHIFT) & NODE_L2DIV_REF_MARK;

	ctrl = (u64)readl(LOONGSON_CONF_REG(0x1b0) + 8);
	l2div_out = (ctrl >> NODE_L2DIV_OUT_SHIFT) & NODE_L2DIV_OUT_MARK;
	mult = l2div_loopc;
	div = l2div_ref * l2div_out;
	pll = (u64)CPUPLL_IN;
	pll = pll * mult;
	do_div(pll, div);
	gd->cpu_clk = pll;

	/* ddr gpu hda clk */
	ctrl = (u64)readl(LOONGSON_CONF_REG(0x1c0));
	ctrl = ctrl | ((u64)readl(LOONGSON_CONF_REG(0x1c0) + 4) << 32);
	l1div_out = (ctrl >> DDR_L1DIV_OUT_SHIFT) & DDR_L1DIV_OUT_MARK;
	l1div_loopc = (ctrl >> DDR_L1DIV_LOOPC_SHIFT) & DDR_L1DIV_LOOPC_MARK;
	l1div_ref = (ctrl >> DDR_L1DIV_REF_SHIFT) & DDR_L1DIV_REF_MARK;
	mult = l1div_loopc;
	div = l1div_ref * l1div_out;
	pll = (u64)DDRPLL_IN;
	pll = pll * mult;
	do_div(pll, div);
	gd->mem_clk = pll;

	gd->bus_clk = BUSPLL_IN;

	gd->arch.pll_clk = LS7A_SYS_CLK;
}

/* arch specific CPU init after DM */
int arch_cpu_init_dm(void)
{
	int ret;
	struct udevice *dev;

	ret = uclass_get_device(UCLASS_CLK, 0, &dev);
	if (ret) {
		printf("clk-uclass not found\n");
		return 0;
	}

	return 0;
}

int mach_cpu_init(void)
{
	calc_clocks();

	return 0;
}

int dram_init(void)
{
#if 0
	gd->ram_size = get_ram_size ((long *)CONFIG_SYS_SDRAM_BASE, CONFIG_SYS_SDRAM_SIZE);
#else
	if (fdtdec_setup_mem_size_base() != 0)
		return -EINVAL;
#endif
	return 0;
}

int dram_init_banksize(void)
{
	fdtdec_setup_memory_banksize();

	return 0;
}

void board_add_ram_info(int use_default)
{
	register int raw_memsz asm ("k1");

	gd->arch.memorysize_total = raw_memsz & 0xff;
#ifdef CONFIG_CPU_LOONGSON3A4000
	gd->arch.memorysize_total = gd->arch.memorysize_total << 30;
#else
	gd->arch.memorysize_total = gd->arch.memorysize_total << 29;
#endif
	puts("(can be use by u-boot)");
	putc('\n');
	puts("DRAM Total: ");
	print_size(gd->arch.memorysize_total, "");
}

#ifdef CONFIG_DISPLAY_CPUINFO
const char *get_core_name(void)
{
	u32 proc_id;
	char *str;

	proc_id = read_c0_prid() & 0xffff;
	switch (proc_id) {
	case 0x00006309:
		str = "LS3A3000 R3_0";
		break;
	case 0x0000630a:
	case 0x0000630d:
		str = "LS3A3000 R3_1";
		break;
	case 0x0000c001:
		str = "LS3A4000";
		break;
	default:
		str = "Unknown            ";
		sprintf(str, "Unknown %x", proc_id);
	}

	return str;
}

int print_cpuinfo(void)
{
	printf("Core: %s\n", get_core_name());
	printf("Speed: Cpu @ %ld MHz/ Mem @ %ld MHz/ Bus @ %ld MHz\n",
			gd->cpu_clk/1000000, gd->mem_clk/1000000, gd->bus_clk/1000000);
	return 0;
}
#endif
